/***********************************************************
 *
 * Gaoth
 * Copyright (c) 2011- Marcus Lundgren
 * 
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the authors be held liable for any damages arising from
 * the use of this software.
 * 
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it freely,
 * subject to the following restrictions:
 *
 *  1. The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *
 *  2. Altered source versions must be plainly marked as such,
 *     and must not be misrepresented as being the original software.
 *
 *  3. This notice may not be removed or altered from any source distribution.
 *
 ***********************************************************/

#include "network_manager.h"

#include <iostream>
#include <sstream>

#define DEFAULT_SOCKET 4567

int client_id = 0;
int temp = 0;

NetworkManager::NetworkManager( Model *model )
: _model(model)
{
    // Create the server socket, which listens at the given port for
    // new connections and updates
    if( _listener.listen( DEFAULT_SOCKET ) != sf::Socket::Done )
        std::cout << "ERROR: NetworkManager: Unable to create socket\n";
    else
        _selector.add( _listener );
}

NetworkManager::~NetworkManager()
{
    // Close the socket
    _listener.close();
    
    // The model should not be deleted here, just NULL the pointer
    _model = 0;
}

void NetworkManager::update()
{
    // TODO
    //std::cout << "NetMan::update\n";
	_selector.wait();

	// We've got a new connection
	if( _selector.isReady( _listener ) )
	{
		sf::TcpSocket *client_socket = new sf::TcpSocket();
		_listener.accept( *client_socket );
		std::cout << "Client connected!\n";

		// TODO
		// - The client_id should be the account id!

		Client *client = new Client( ++client_id, client_socket );

		// Add the client to the model
		_model->add_client( client );

		// Add the socket to the listener
		_selector.add( *client->get_socket() );

		// Send back the id to the client
	}
	// We've got an update from a client socket
	else
	{
        std::cout << "Got something from a client...\n";
        ClientVector removal_list;
        ClientVector::iterator it;
        sf::Packet packet;

        _model->acquire();

		ClientVector clients = _model->get_clients();

		for( it = clients.begin() ; it != clients.end() ; ++it )
		{
			sf::TcpSocket *socket = (*it)->get_socket();
			if( _selector.isReady( *socket ) )
			{
				if( socket->receive( packet ) == sf::Socket::Done )
				{
				    std::cout << "Got something from a client...\n";
					// TODO
				}
				// Something went wrong, disconnect the client
				else
				{
                    std::cout << "Trying to remove a client...";
					removal_list.push_back( *it );
					_selector.remove( *socket );
					std::cout << "DONE!\n";
				}
			}
		}
		for( it = removal_list.begin() ; it != removal_list.end() ; ++it )
			_model->remove_client( *(*it)->get_socket() );
        _model->release();
		std::cout << "--DONE--\n";
	}
}

void NetworkManager::send_updates()
{
    sf::Packet p;

    gth::shared::network::ModelPayload mp;
    ClientVector::const_iterator it;

    _model->acquire();
    const ClientVector clients = _model->get_clients();
    const std::vector<NPCEntity> &entities = _model->get_npc_entities();

    //std::vector<Client*> clients_to_remove;

    for( unsigned int i = 0 ; i < entities.size() ; ++i )
        mp.entities.push_back( static_cast<gth::shared::data::BaseEntity>(entities[i]) );

    _pkt_mgr.to_packet( mp, p );
    //std::clog << "herp\n";
    //std::cout << "Number of clients == " << clients.size() << '\n';
    for( it = clients.begin() ; it != clients.end() ; ++it )
    {
        std::cout << "Sending update to client<" << (*it)->get_id() << ">...";
        if( (*it)->get_socket()->send(p) == sf::Socket::Done )
            std::cout << "DONE!\n";
        else
            std::cout << "FAILED!\n";
    }
    _model->release();
}

void NetworkManager::send( const gth::shared::network::BasePayload* /**payload*/, const int /*client_id*/ )
{
  // TODO
}
